'''
Created on Mar 12, 2013

@author: Petko
'''
from django.views.generic.base import View
from sent.model.UserDBManager import UserDBManager

from sent.model.entities.user import User
from sententialyzer.settings import DbSession, SENTENTIALYZER_AUTHENTICATOR,\
    SENTENTIALYZER_ACCESS_TOKEN_EXPIRATION_TIME_SEC, DbSessionRO
from sent.utils import find_implementation, CommonResult, ErrorIds, AccessToken
from sent.auth.authenticator import Authenticator
import time
from sent.utils.result_printer import getPrinter
from sent.utils.permissions import parseScopeList
import sys

AuthenticatorClass = find_implementation(SENTENTIALYZER_AUTHENTICATOR, Authenticator)


class Authorize(View):
    '''
    POST /auth/authorize
    Parameters
    - user_id Unique user identifier
    - password 
    - scope String which contains a list of permissions separated by spaces. Permission to certain base opinion is encoded with <opinion id>:<permission>
    
    Result
    
    In case of success - status code 200 and an object containing
    - access_token access token which will be used in subsequent queries to Sententialyzer
    - expires_in Period of time after the call to Authorize in which the access token is valid
    - user_data Some data provided by the system in which Sententialyzer is integrated
    '''
    def __init__(self):
        self.authenticator = AuthenticatorClass()
        

    def post(self, *args, **kwargs):
        user_id = self.request.POST.get('user_id', None)
        password = self.request.POST.get('password', None)
        scopeListStr = self.request.POST.get('scope', None)
        
        result = CommonResult()
        
        if user_id and password and scopeListStr:
            authenticateResult, externalData = self.authenticator.authenticate(user_id, password)
            if authenticateResult:
                userDb = UserDBManager(True)
                try:
                    user = userDb.getUserByExtId(user_id)
                    if user:
                        externalData = user.external_data 
                        pass
                    else:
                        if DbSession != DbSessionRO:
                            #close the current RO session and start RW session in case of scaling by replication 
                            userDb.closeSession()
                            userDb = UserDBManager(False)
                            
                        user = User()
                        user.external_data = externalData
                        user.external_id = user_id
                        user.rating = 0
                        userDb.createUser(user)
                        
                        userDb.commitSession()
                        
                    
                    accessToken = AccessToken()
                    accessToken.owner_id = str(user.getUUID())
                    accessToken.owner_ext_id = unicode(user.external_id)
                    accessToken.expiration_time = int(time.time()) + SENTENTIALYZER_ACCESS_TOKEN_EXPIRATION_TIME_SEC
                    
                    #todo check that the user has permission for this scope
                    
                    scopeList = parseScopeList(scopeListStr)
                    groupScopeSet=set()
                    scopesAreValid=True
                    neededOpinions = []
                    neededViews = []
                    for scope in scopeList:
                        if scope.isGroupScope:
                            groupScopeSet.add(scope)
                        elif scope.isOpinionScope:
                            if not scope.isDefaultOpScope:
                                neededOpinions.append(scope.opinionBinaryId)
                        elif scope.isViewScope:
                            if not scope.isDefaultViewScope:
                                neededViews.append(scope.viewId)
                        else:
                            scopesAreValid=False
                            result.result = "Invalid scope %s" % scope.data
                    
                    if scopesAreValid:
                        if len(groupScopeSet):
                            groupPermissions = userDb.getGroupPermissions(user.id)
                            if not groupScopeSet.issubset(groupPermissions):
                                scopesAreValid = False
                                result.result = 'You cannot ask for this group scope'
                                
                        if len(neededOpinions):
                            opinionPermissionDict = userDb.getOpinionsPermissions(user.id, neededOpinions)
                            for scope in scopeList:
                                if scope.isOpinionScope and not scope.isDefaultOpScope:
                                    scopeReceived = opinionPermissionDict.get(scope.opinionId, None)
                                    if scopeReceived:
                                        if not scope.opinionPermissions.issubset(scopeReceived.opinionPermissions):
                                            scopesAreValid = False
                                            result.result = 'You cannot ask for this scope for opinion id %s' % scope.opinionId
                                    else:
                                        scopesAreValid = False
                                        result.result = 'No permission for opinion id %s' % scope.opinionId
                                        
                        if len(neededViews):
                            viewPermissionDict = userDb.getViewPermissions(user.id, neededViews)
                            for scope in scopeList:
                                if scope.isViewScope and not scope.isDefaultViewScope:
                                    scopeReceived = viewPermissionDict.get(scope.viewId, None)
                                    if scopeReceived:
                                        if not scope.viewPermissions.issubset(scopeReceived.viewPermissions):
                                            scopesAreValid = False
                                            result.result = 'You cannot ask for this scope for view id %s' % scope.viewId
                                    else:
                                        scopesAreValid = False
                                        result.result = 'No permission for view id %s' % scope.viewId
                                        
                        if scopesAreValid:
                        
                            accessToken.scopes = scopeListStr
                            
                            accessToken.encrypt()
                            
                            result.errorId = ErrorIds.SUCCESS
                            result.result = {
                                             'access_token': accessToken.toString(),
                                             'expires_in': SENTENTIALYZER_ACCESS_TOKEN_EXPIRATION_TIME_SEC
                                            }
                            
                            if externalData:
                                result.result['user_data'] = externalData
                            
                        else:
                            result.errorId = ErrorIds.FORBIDDEN
                            
                    else:
                        result.errorId = ErrorIds.BADREQUEST
                    
                except:
                    result.errorId = ErrorIds.INTERNAL_ERROR
                    userDb.rollbackSession()
                    raise
                finally:
                    userDb.closeSession()
        else:
            result.errorId = ErrorIds.UNAUTHORIZED
            result.result = 'Authentication failed'
        
        return getPrinter().printResult(result)
    